package org.NooLab.glue.transaction;

import java.io.Serializable;




/**
 * 
 *  
 */
public class Transaction implements Serializable{

	transient private static final long serialVersionUID = 7307647813784791103L;
	// =================================

	// constants ......................


	transient public static int TS_PRE = -1;
	// internal means: "internal to process", code value will be added to 100
	transient public static int TS_ERR_INTERNAL = -100; 
	transient public static int TS_ERR_UNKNOWN  = -91;
	
	transient public static int TS_ERR_SRV_CAPACITY = -50;
	
	
	transient public static int TS_ERR_TRANSINVALID = -22;
	transient public static int TS_ERR_BUFFNOTFOUND = -20;
	transient public static int TS_ERR_BUFFINVALID  = -19;
	transient public static int TS_ERR_TIMEDOUT     = -18;
	
	transient public static int TS_ERR_STOPPED      = -17;
	
	transient public static int TS_TASK_NOTRANSFER  = -14;
	
	transient public static int TS_ERR_NOCONTEXT    = -11;
	transient public static int TS_ERR_NOMATCH      = -10;
	transient public static int TS_ERR_NO           =  0;
	
	transient public static int TS_OPENED           = 1;
	transient public static int TS_ID_ASSIGNED      = 2;
	
	transient public static int TS_ID_CONFIRMED     = 5;
	
	
	transient public static int TS_DATA_ACCEPTING   = 7;
	
	transient public static int TS_TASK_CONFIRMED   = 8;
	
	transient public static int TS_TASK_DENIED      = 9;
	
	
	transient public static int TS_TASK_ACCEPTED    = 15; // accepted by the BOARD (??RECEPTOR??)
	transient public static int TS_TASK_MATCHING    = 16;
	
	
	transient public static int TS_TASK_DELAYED     = 18;
	
	transient public static int TS_TASK_STARTDELIVERY = 19;
	
	transient public static int TS_TASK_RELAYING    = 20;
	transient public static int TS_TASK_DELIVERING  = 21;
	
	transient public static int TS_TASK_DELIVERED   = 23;
	
	transient public static int TS_TASK_DIGESTING   = 24;
	
	
	transient public static int TS_TASK_CANCELED    = 25;
	transient public static int TS_TASK_TIMEDOUT    = 26;
	transient public static int TS_TASK_UNKNOWNFAIL = 27;
	transient public static int TS_TASK_UNKNOWN     = 28;
	
	transient public static int TS_RECEPTOR_RUN     = 30;

	transient public static int TS_RECEPTOR_RETURNS = 35;
	transient public static int TS_RESULTS_BUFFERED = 37;
	transient public static int TS_RESULTS_RETURNED = 39;  
	
	
	transient public static int TS_CLOSED   = 99;
	transient public static int TS_ARCHIVED = 100;

	
	transient public static int TD_UNSPEC = -1;
	transient public static int TD_STRING = 1;
	transient public static int TD_INT = 5;
	transient public static int TD_NUM = 6;
	transient public static int TD_OBJ = 9;
	transient public static int TD_RELAY = 20; // 

	// transmitResourceAsReference 
	transient public static int DT_REF = 1;
	transient public static int DT_DAT = 9;

	
	transient public int ageThresholdForDigestingTasks = 3 ; // this is in days
	 
	
	// main variables / properties ....

	
	int index = -1;
	
	long transactionID = 0;
	long instanceID = 0;
	
	String roleOfInitializer = "";
	String nameOfInitializer = "";
	
	boolean isClosed = false;
	
	int transactState = TS_PRE ;
	int transactErrorState = TS_ERR_NO ;
	String transactStateDescription = "";
	transient boolean stateChanged=false;
	
	String dataObject ;
	String resultObject ;
	
	/** that's a GUID issued by the initiator of a transaction, which provides security against unjustified delivery */
	String clientSecret="";
	
	int deliveryPort = -1 ;
	long timeofDelivery = 0; // by SOURCE to BOARD !
	
	int taskRelayTrialCounter; // counts relay acts board -> receptor, will be reset upon delay to other MessageBoard ! 
	int maxRelayTrialCount = 100 ;
	
	/** if 0 -> no automatic expiry, if >0 message/transaction may expire while waiting for a RECEPTOR <br/>
	 *  needs to be given as an absolute values in millis: the issuer is responsible on itself!
	 *  as an "age", it described time since delivery
	 */
	long ageOfExpiry = 0;
	
	int datatype = TD_UNSPEC;
	String contentType = "" ;
	String dataEncoding = "" ;
	int dataCompression = 0 ;
	
	// this describes the kind of relationships between participants of the MessageBoard;
	// it is mirrored from the InstanceFlavor object 
	// it influences the behavior of the MessageBoard, the Source and of the Receptor
	int linkRelation ;
	int relationItemID ; 
	int relationGroupID ;
	
	int relayByContext;
	
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	public Transaction(){
		transactState = TS_OPENED ;
	}
	
	// essentially, this is creating a clone
	public Transaction( Transaction inTransactionAsTemplate ){
		copy( inTransactionAsTemplate, this);
	}
	
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	
	
	public static int encodeTransactionCode( String wordedCode ){
		int code = -1 ;
		 													    	
		if  (wordedCode.contentEquals("TS_PRE"))              { code= -1; } ; 
		if  (wordedCode.contentEquals("TS_ERR_INTERNAL"))     { code= -100; }  ;
		if  (wordedCode.contentEquals("TS_ERR_UNKNOWN"))      { code= -27; } ;  
		
		if  (wordedCode.contentEquals("TS_ERR_BUFFNOTFOUND")) { code= -20; } ;
		if  (wordedCode.contentEquals("TS_ERR_BUFFINVALID"))  { code= -19; } ;
		
		if  (wordedCode.contentEquals("TS_ERR_STOPPED"))      { code= -17; }  ;
		if  (wordedCode.contentEquals("TS_TASK_NOTRANSFER"))  { code= -14; }  ;
		if  (wordedCode.contentEquals("TS_ERR_NOMATCH"))      { code= -10; }  ;
		
		
		if  (wordedCode.contentEquals("TS_OPENED")) 		  { code= 1; }  ;
		if  (wordedCode.contentEquals("TS_ID_ASSIGNED")) 	  { code= 2; }  ; 
		if  (wordedCode.contentEquals("TS_ID_CONFIRMED")) 	  { code= 5; }  ;
		
		if  (wordedCode.contentEquals("TS_DATA_ACCEPTING"))   { code= 7; }  ;
		if  (wordedCode.contentEquals("TS_TASK_CONFIRMED"))   { code= 8; }  ;
		if  (wordedCode.contentEquals("TS_TASK_DENIED")) 	  { code= 9; }  ;

		if  (wordedCode.contentEquals("TS_TASK_ACCEPTED"))    { code= 10; }  ;

		if  (wordedCode.contentEquals("TS_TASK_DELAYED")) 	  { code= 12; }  ;

		if  (wordedCode.contentEquals("TS_TASK_DELIVERING"))  { code= 14; }  ;
		if  (wordedCode.contentEquals("TS_TASK_DELIVERED"))   { code= 15; }  ;
		if  (wordedCode.contentEquals("TS_TASK_DIGESTING"))   { code= 16; }  ;
		if  (wordedCode.contentEquals("TS_TASK_TIMEDOUT"))    { code= 18; }  ; 
		if  (wordedCode.contentEquals("TS_TASK_CANCELED"))    { code= 17; }  ; 
		
		if  (wordedCode.contentEquals("TS_RECEPTOR_RUN")) 	  { code= 20; }  ;
		if  (wordedCode.contentEquals("TS_RECEPTOR_RETURNS")) { code= 25; }  ;
		if  (wordedCode.contentEquals("TS_RESULTS_BUFFERED")) { code= 26; }  ;
		if  (wordedCode.contentEquals("TS_RESULTS_RETURNED")) { code= 28; }  ;
		if  (wordedCode.contentEquals("TS_CLOSED")) 		  { code= 99; }  ;
		if  (wordedCode.contentEquals("TS_ARCHIVED")) 	 	  { code= 100; } ; 
		
		return code ;
	}
	
	
	public static String decodeTransactionCode( int code ){
		String wordedCode="" ;
		 
	    switch (code) {
	    case -1 :
	    	wordedCode="TS_PRE"; break;
	    case -100 :
	    	wordedCode="TS_ERR_INTERNAL"; break; 
	    case -91 :
	    	wordedCode="TS_ERR_UNKNOWN"; break; 
	    case -50 :
	    	wordedCode="TS_ERR_SRV_CAPACITY"; break;


	    case -22 :
	    	wordedCode="TS_ERR_TRANSINVALID"; break;

	    case -20 :
	    	wordedCode="TS_ERR_BUFFNOTFOUND"; break;
	    case -19 :
	    	wordedCode="TS_ERR_BUFFINVALID"; break;
	    case -18 :
	    	wordedCode="TS_ERR_TIMEDOUT"; break;
	    case -17 :
	    	wordedCode="TS_ERR_STOPPED"; break;
	    case -14 :
	    	wordedCode="TS_TASK_NOTRANSFER"; break;
	    case -10 :
	    	wordedCode="TS_ERR_NOMATCH"; break;

	    case 1 :
	    	wordedCode="TS_OPENED"; break;
	    case 2 :
	    	wordedCode="TS_ID_ASSIGNED"; break;
	    case 5 :
	    	wordedCode="TS_ID_CONFIRMED"; break;
	    case 7 :
	    	wordedCode="TS_DATA_ACCEPTING"; break;
	    case 8 :
	    	wordedCode="TS_TASK_CONFIRMED"; break;
	    case 9 :
	    	wordedCode="TS_TASK_DENIED"; break;
	     
	    case 10 :
	    	wordedCode="TS_TASK_ACCEPTED"; break;
	    case 11 :
	    	wordedCode="TS_TASK_DELAYED"; break;
	    case 12 :
	    	wordedCode="TS_TASK_STARTDELIVERY"; break;
	    case 13 :
	    	wordedCode="TS_TASK_RELAYING"; break;
	    case 14 :
	    	wordedCode="TS_TASK_DELIVERING"; break;
	    case 15 :
	    	wordedCode="TS_TASK_DELIVERED"; break;
	    case 16 :
	    	wordedCode="TS_TASK_DIGESTING"; break;
	    case 17 :
	    	wordedCode="TS_TASK_CANCELED"; break;
	    case 18 :
	    	wordedCode="TS_TASK_TIMEDOUT"; break;
	    case 19 :
	    	wordedCode="TS_TASK_UNKNOWNFAIL"; break;
	    case 20 :
	    	wordedCode="TS_TASK_UNKNOWN"; break;
	    case 22 :
	    	wordedCode="TS_RECEPTOR_RUN"; break;
	    case 25 :
	    	wordedCode="TS_RECEPTOR_RETURNS"; break;
	    case 26 :
	    	wordedCode="TS_RESULTS_BUFFERED"; break;
	    case 28 :
	    	wordedCode="TS_RESULTS_RETURNED"; break;
	    case 99 :
	    	wordedCode="TS_CLOSED"; break;
	    case 100 :
	    	wordedCode="TS_ARCHIVED"; break;

	    default:
	    	wordedCode="TS_UNKNOWN" ;
	    }


		// if (code== 2 ) { wordedCode=""; }
		 /*
		if (code== -1 ){ wordedCode="TS_PRE"; }
		if (code== -27){ wordedCode="TS_ERR_UNKNOWN"; }
		
		if (code==-100){ wordedCode="TS_ERR_INTERNAL"; }
		if (code== -20){ wordedCode="TS_ERR_BUFFNOTFOUND"; }
		if (code== -19){ wordedCode="TS_ERR_BUFFINVALID"; }
		if (code== -17){ wordedCode="TS_ERR_STOPPED"; }
		if (code== -14){ wordedCode="TS_TASK_NOTRANSFER"; }  
		if (code== -10){ wordedCode="TS_ERR_NOMATCH"; }
		 
		 if (code== 1 ) { wordedCode="TS_OPENED"; }
		if (code== 2 ) { wordedCode="TS_ID_ASSIGNED"; }
		if (code== 5 ) { wordedCode="TS_ID_CONFIRMED"; }
		
		
		if (code== 7 ) { wordedCode="TS_DATA_ACCEPTING"; }
		if (code== 8 ) { wordedCode="TS_TASK_CONFIRMED"; }
 
		if (code== 9 ) { wordedCode="TS_TASK_DENIED"; }
		
		if (code== 10) { wordedCode="TS_TASK_ACCEPTED"; }

		
		if (code== 12) { wordedCode="TS_TASK_DELAYED"; }
		if (code== 14) { wordedCode="TS_TASK_DELIVERING"; }  
		if (code== 15) { wordedCode="TS_TASK_DELIVERED"; }  
		if (code== 16) { wordedCode="TS_TASK_DIGESTING"; }  
		if (code== 18) { wordedCode="TS_TASK_TIMEDOUT"; }
		if (code== 17) { wordedCode="TS_TASK_CANCELED"; }         
		
			   
 
		if (code== 20) { wordedCode="TS_RECEPTOR_RUN"; }
		if (code== 25) { wordedCode="TS_RECEPTOR_RETURNS"; }
		if (code== 26) { wordedCode="TS_RESULTS_BUFFERED"; }
		if (code== 28) { wordedCode="TS_RESULTS_RETURNED"; }
		if (code== 99) { wordedCode="TS_CLOSED"; }
		if (code== 100){ wordedCode="TS_ARCHIVED"; }
		*/
		
		return wordedCode;
	}
	

	private void copy( Transaction from, Transaction to ){
		
		
		
		to.index = from.index ;
		
		to.transactionID = from.transactionID ;
		to.instanceID = from.instanceID ;
		
		to.roleOfInitializer = from.roleOfInitializer ;
		to.nameOfInitializer = from.nameOfInitializer ;
		
		to.isClosed = from.isClosed ;
		
		to.transactState = from.transactState ;
		to.transactErrorState = from.transactErrorState ;
		to.transactStateDescription = from.transactStateDescription ;
		 
		
		to.dataObject = from.dataObject ;
		to.resultObject = from.resultObject ;
		
		to.clientSecret= from.clientSecret ;
		
		to.deliveryPort = from.deliveryPort  ;
		to.timeofDelivery = from.timeofDelivery ; 
		
		to.taskRelayTrialCounter = from.taskRelayTrialCounter ;  
		to.maxRelayTrialCount = from.maxRelayTrialCount  ;
		
		to.ageOfExpiry = from.ageOfExpiry ;
		
		to.datatype = from.datatype ;
		to.contentType = from.contentType ;
		to.dataEncoding = from.dataEncoding ;
		to.dataCompression = from.dataCompression ;
		
		to.linkRelation = from.linkRelation ;
		to.relationItemID = from.relationItemID ; 
		to.relationGroupID = from.relationGroupID ;
		
		to.relayByContext = from.relayByContext ;
	}
	
	
	public boolean isAlive( int relaxed ){
		
		boolean rB = true;
		long now;
		
		
		rB = transactErrorState == TS_ERR_NO;
		
		if (relaxed<=0){
			if (rB){ rB = ( transactState != TS_TASK_DENIED) ; }
		}
		if (rB){ rB = ( transactState != TS_TASK_CANCELED) ; }
		if (rB){ rB = ( transactState <  TS_RESULTS_RETURNED) ; }
		if (rB){ rB = ( transactState >= TS_TASK_CONFIRMED) ; }
		if (rB){ rB = ( transactState != TS_TASK_TIMEDOUT ) ; }
		
		if (rB){
			 if (ageOfExpiry >0 ){
				 now = System.currentTimeMillis();
				 if (ageOfExpiry < 259200000){ // == 3 days
					 // relative value
					 if (timeofDelivery>0){
						 
						 rB = ( now - timeofDelivery < ageOfExpiry);
					 }
					 
				 }else{
					 rB = ( ageOfExpiry - now > 0); 
				 }
			 }
		}
		
		return rB;
	}
	
	
	/**
	 * if the transaction has its own expiry (>0), this will be checked !
	 * Otherwise the provided value will be taken<br/>
	 *  
	 *  scale : sec, min, hour, days
	 *  
	 * @param agevalue
	 * @param scale
	 * @return
	 */
	public boolean isOld( long agevalue, String scale ){
		
		int factor=1;
		
		
		scale = scale.toLowerCase();
		 
		
		if ((scale.contains("day")) || (scale.startsWith("d"))){
			factor = 24*60*60*1000 ;
		}

		if ((scale.contains("hour")) || (scale.startsWith("h"))){
			factor = 60*60*1000 ;
		}
		if ((scale.contains("min")) || (scale.startsWith("mi"))){
			factor = 60*1000 ;
		}
		if ((scale.contains("sec")) || (scale.startsWith("s"))){
			factor = 1000 ;
		}
		
		agevalue = agevalue * factor;
		
		return isOld(agevalue);
	}
	
	public boolean isOld( long millisAge ){
		boolean rB=false;
		long traxpiry = -1, tdiff;
		long now = System.currentTimeMillis();
		
		traxpiry = this.ageOfExpiry;
		
		if (timeofDelivery<1001){
			timeofDelivery = System.currentTimeMillis() ;
		}
		
		tdiff = now - getTimeofDelivery();
		
		if (traxpiry<=0){

			if (getTimeofDelivery() > 1000) {
				if (tdiff > millisAge) {
					rB = true;
				}
			}
		}else{

			if (getTimeofDelivery() > 1000) {
			
				if ( now - getTimeofDelivery() > traxpiry) {
					rB = true;
				}
			}
		
			
		}
		
		return rB;
	}
	
	public long getAge( int byState ){
		boolean rB=false;
		long millisAge=0 ;
		
		if (timeofDelivery<1001){
			timeofDelivery = System.currentTimeMillis() ;
		}
		millisAge = (System.currentTimeMillis() - getTimeofDelivery() );
		
		if ( byState <= 1 ){
			millisAge = (System.currentTimeMillis() - getTimeofDelivery() );
		}
		if ( byState <= 2 ){
			millisAge = (System.currentTimeMillis() - getTimeofDelivery() );
		}
		
		return millisAge ;
	}
	
	
	
	// ------------------------------------------------------------------------
	
	synchronized public void setState( int stateval ){
		if (stateval <= 100){
			transactState = stateval ;
		}
	}

	public void setErrorState( int stateval ){
		if (stateval <= 100){
			transactErrorState = stateval ; 
		}
	}
	
	
	public long getInstanceID(){ 
		
		return instanceID;
	}

	public long getTransactionID(){ 
		
		return transactionID;
	}

	public void setStarterRole( String str){
		roleOfInitializer = str ;
	}
	
	 
	
	public void setInstanceID( long idval ){
		instanceID = idval ;
	}



	public String getRoleOfInitializer() {
		return roleOfInitializer;
	}



	public void setRoleOfInitializer(String roleOfInitializer) {
		this.roleOfInitializer = roleOfInitializer;
	}



	public void setTransactionID(long transactionID) {
		this.transactionID = transactionID;
	}


	public String getNameOfInitializer() {
		return nameOfInitializer;
	}


	public void setNameOfInitializer(String nameOfInitializer) {
		this.nameOfInitializer = nameOfInitializer;
	}


	public boolean isClosed() {
		return isClosed;
	}


	public void setClosed(boolean isClosed) {
		this.isClosed = isClosed;
	}


	public int getTransactState() {
		if (timeofDelivery<1001){
			timeofDelivery = System.currentTimeMillis() ;
		}
		return transactState;
	}


	public void setTransactState(int state) {
		transactState = state;
		stateChanged = true;
	}


	public int getTransactErrorState() {
		return transactErrorState;
	}


	public void setTransactErrorState(int transactErrorState) {
		this.transactErrorState = transactErrorState;
	}


	public String getTransactStateDescription() {
		return transactStateDescription;
	}




	public void setTransactStateDescription(String description) {
		transactStateDescription = description;
		stateChanged = true;
	}




	public String getContentType() {
		return contentType;
	}


	public void setContentType(String contentType) {
		this.contentType = contentType;
	}


	public void setRelayByContext(int relaybycontext) {
		relayByContext = relaybycontext;
	}
	
	
	public int getRelayByContext() {
		return relayByContext;
	}


	public int getType() {
		return datatype;
	}


	public void setType(int type) {
		this.datatype = type;
	}


	public int getIndex() {
		return index;
	}


	public void setIndex(int index) {
		this.index = index;
	}


	public String getClientSecret() {
		return clientSecret;
	}




	public void setClientSecret(String clientSecret) {
		this.clientSecret = clientSecret;
	}




	public int getDeliveryPort() {
		return deliveryPort;
	}


	public void setDeliveryPort(int deliveryPort) {
		this.deliveryPort = deliveryPort;
	}

	
	public void incTaskRelayTrialCounter(){
		taskRelayTrialCounter++;
	}
	
	public int getMaxRelayTrialCount(){
		return maxRelayTrialCount;
	}
	
	public int getTaskRelayTrialCounter(){
		return taskRelayTrialCounter;
	}

	public long getTimeofDelivery() {
		return timeofDelivery;
	}


	public void setTimeofDelivery(long timeofDelivery) {
		this.timeofDelivery = timeofDelivery;
	}


	public long getAgeOfExpiry() {
		return ageOfExpiry;
	}


	public void setAgeOfExpiry(long ageOfExpiry) {
		this.ageOfExpiry = ageOfExpiry;
	}


	public String getDataObject() {
		return dataObject;
	}


	public void setDataObject(String dataObject) {
		this.dataObject = dataObject;
	}


	public String getResultObject() {
		return resultObject;
	}


	public void setResultObject(String resultObject) {
		this.resultObject = resultObject;
	}


	public int getDatatype() {
		return datatype;
	}


	public void setDatatype(int datatype) {
		this.datatype = datatype;
	}


	public int getDataCompression() {
		return dataCompression;
	}


	public void setDataCompression(int dataCompression) {
		this.dataCompression = dataCompression;
	}


	public String getDataEncoding() {
		return dataEncoding;
	}


	public void setDataEncoding(String dataEncoding) {
		this.dataEncoding = dataEncoding;
	}


	public int getLinkRelation() {
		return linkRelation;
	}


	public void setLinkRelation(int linkRelation) {
		this.linkRelation = linkRelation;
	}


	public int getRelationItemID() {
		return relationItemID;
	}


	public void setRelationItemID(int relationItemID) {
		this.relationItemID = relationItemID;
	}


	public int getRelationGroupID() {
		return relationGroupID;
	}


	public void setRelationGroupID(int relationGroupID) {
		this.relationGroupID = relationGroupID;
	}


	
	
	
}
